package com.hww.base.util;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.YuvImage;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import com.hww.base.util.tool.MemoryConstants;

public class ImageUtils {

	/**
	 * 截取指定View下的控件的图片 如果想截取Activity的图片，只需要将Activity的根布局传入即可
	 *
	 * @param view 控件View或者activity
	 * @return Bitmap
	 */
	public static Bitmap captureView(View view) {
		// 根据View的宽高创建一个空的Bitmap
		Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
		// 利用该Bitmap创建一个空的Canvas
		Canvas canvas = new Canvas(bitmap);
		// 绘制背景(可选)
		canvas.drawColor(Color.WHITE);
		// 将view的内容绘制到我们指定的Canvas上
		view.draw(canvas);
		return bitmap;
	}

	/**
	 * 获取位图bitmap的RGB数据
	 * 
	 * @param bitmap
	 * @return
	 */
	public static byte[] rgbFrombitmap(Bitmap bitmap) {
		if (bitmap == null) {
			return null;
		}
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		int size = width * height;

		int pixels[] = new int[size];
		bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

		byte[] data = convertColorToByte(pixels);
		return data;
	}

	/**
	 * 像素数组转化为RGB数组
	 * 
	 * @param color 像数组
	 * @return 二进制数组
	 */
	private static byte[] convertColorToByte(int color[]) {
		if (color == null) {
			return null;
		}

		byte[] data = new byte[color.length * 3];
		for (int i = 0; i < color.length; i++) {
			data[i * 3] = (byte) (color[i] >> 16 & 0xff);
			data[i * 3 + 1] = (byte) (color[i] >> 8 & 0xff);
			data[i * 3 + 2] = (byte) (color[i] & 0xff);
		}

		return data;

	}

	/**
	 * byte[] data保存的是纯RGB的数据，而非完整的图片文件数据
	 * 
	 * @param data   RGB数据
	 * @param width  宽
	 * @param height 高
	 * @return Bitmap
	 */
	public static Bitmap bitmapFromrgb(byte[] data, int width, int height) {
		int[] colors = convertByteToColor(data);
		if (colors == null) {
			return null;
		}
		Bitmap bmp = null;
		try {
			bmp = Bitmap.createBitmap(colors, 0, width, width, height, Bitmap.Config.ARGB_8888);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return bmp;
	}

	/**
	 * 将纯BGR数据数组转化成int像素数组
	 *
	 * @param data BGR数据
	 * @return
	 */
	private static int[] convertByteToColor(byte[] data) {
		int size = data.length;
		if (size == 0) {
			return null;
		}
		int arg = 0;
		if (size % 3 != 0) {
			arg = 1;
		}
		// 一般RGB字节数组的长度应该是3的倍数，
		// 不排除有特殊情况，多余的RGB数据用黑色0XFF000000填充
		int[] color = new int[size / 3 + arg];
		int red, green, blue;
		int colorLen = color.length;
		if (arg == 0) {
			// BGR数据转RGBA
			for (int i = 0; i < colorLen; ++i) {
				red = convertByteToInt(data[i * 3 + 2]);
				green = convertByteToInt(data[i * 3 + 1]);
				blue = convertByteToInt(data[i * 3]);

				// 获取RGB分量值通过按位或生成int的像素值
				color[i] = (red << 16) | (green << 8) | blue | 0xFF000000;
			}
		} else {
			// RGB数据转RGBA
			for (int i = 0; i < colorLen - 1; ++i) {
				red = convertByteToInt(data[i * 3]);
				green = convertByteToInt(data[i * 3 + 1]);
				blue = convertByteToInt(data[i * 3 + 2]);
				color[i] = (red << 16) | (green << 8) | blue | 0xFF000000;
			}
			// 最后一个像素用黑色填充
			color[colorLen - 1] = 0xFF000000;
		}

		return color;
	}

	/**
	 * 将一个byte数转成int 实现这个函数的目的是为了将byte数当成无符号的变量去转化成int
	 *
	 * @param data BGR数据
	 * @return
	 */
	private static int convertByteToInt(byte data) {

		int heightBit = (int) ((data >> 4) & 0x0F);
		int lowBit = (int) (0x0F & data);
		return heightBit * 16 + lowBit;
	}

	/**
	 * Bitmap to bytes.
	 *
	 * @param bitmap The bitmap.
	 * @param format The format of bitmap.
	 * @return bytes
	 */
	public static byte[] bitmap2Bytes(final Bitmap bitmap, final Bitmap.CompressFormat format) {
		if (null == bitmap || bitmap.isRecycled()) {
			return null;
		}
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bitmap.compress(format, 100, baos);
		return baos.toByteArray();
	}

	/**
	 * Bytes to bitmap.
	 *
	 * @param bytes The bytes.
	 * @return bitmap
	 */
	public static Bitmap bytes2Bitmap(final byte[] bytes) {
		return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
	}

	/**
	 * Drawable to bitmap.
	 *
	 * @param drawable The drawable.
	 * @return bitmap
	 */
	public static Bitmap drawable2Bitmap(final Drawable drawable) {
		if (drawable instanceof BitmapDrawable) {
			BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
			if (bitmapDrawable.getBitmap() != null) {
				return bitmapDrawable.getBitmap();
			}
		}
		Bitmap bitmap;
		if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
			bitmap = Bitmap.createBitmap(1, 1,
					drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		} else {
			bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
					drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		}
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
		drawable.draw(canvas);
		return bitmap;
	}

	/**
	 * Return bitmap.
	 *
	 * @param file The file.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final File file) {
		if (file == null) {
			return null;
		}
		return BitmapFactory.decodeFile(file.getAbsolutePath());
	}

	/**
	 * Return bitmap.
	 *
	 * @param file      The file.
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) {
		if (file == null) {
			return null;
		}
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(file.getAbsolutePath(), options);
		options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
	}

	/**
	 * Return bitmap.
	 *
	 * @param filePath The path of file.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final String filePath) {
		if (isSpace(filePath)) {
			return null;
		}
		return BitmapFactory.decodeFile(filePath);
	}

	/**
	 * 获取缩放后的本地图片
	 *
	 * @param filePath 文件路径
	 * @param width    宽
	 * @param height   高
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromFileDescriptor(String filePath, int width, int height) {
		try {
			FileInputStream fis = new FileInputStream(filePath);
			BitmapFactory.Options options = new BitmapFactory.Options();
			options.inJustDecodeBounds = true;
			BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
			float srcWidth = options.outWidth;
			float srcHeight = options.outHeight;
			int inSampleSize = 1;

			if (srcHeight > height || srcWidth > width) {
				if (srcWidth > srcHeight) {
					inSampleSize = Math.round(srcHeight / height);
				} else {
					inSampleSize = Math.round(srcWidth / width);
				}
			}

			options.inJustDecodeBounds = false;
			options.inSampleSize = inSampleSize;

			return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
		} catch (Exception ex) {
		}
		return null;
	}

	/**
	 * 获取缩放后的本地图片;从输入流中读取文件(网络加载)
	 *
	 * @param ins    输入流
	 * @param width  宽
	 * @param height 高
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromInputStream(InputStream ins, int width, int height) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeStream(ins, null, options);
		float srcWidth = options.outWidth;
		float srcHeight = options.outHeight;
		int inSampleSize = 1;

		if (srcHeight > height || srcWidth > width) {
			if (srcWidth > srcHeight) {
				inSampleSize = Math.round(srcHeight / height);
			} else {
				inSampleSize = Math.round(srcWidth / width);
			}
		}

		options.inJustDecodeBounds = false;
		options.inSampleSize = inSampleSize;

		return BitmapFactory.decodeStream(ins, null, options);
	}

	/**
	 * 获取Resources资源的bitmap对象
	 * 
	 * BitmapFactory.decodeResource 加载的图片可能会经过缩放，该缩放目前是放在 java 层做的，效率比较低，而且需要消耗
	 * java层的内存。因此，如果大量使用该接口加载图片，容易导致OOM错误 BitmapFactory.decodeStream
	 * 不会对所加载的图片进行缩放，相比之下占用内存少，效率更高
	 * 
	 * 这两个接口各有用处，如果对性能要求较高，则应该使用 decodeStream；如果对性能要求不高，且需要 Android
	 * 自带的图片自适应缩放功能，则可以使用 decodeResource
	 * 
	 * @param resources   对象
	 * @param resourcesId 资源Id
	 * @param width       宽
	 * @param height      高
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromResourceStream(Resources resources, int resourcesId, int width, int height) {
		InputStream ins = resources.openRawResource(resourcesId);
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeStream(ins, null, options);
		float srcWidth = options.outWidth;
		float srcHeight = options.outHeight;
		int inSampleSize = 1;

		if (srcHeight > height || srcWidth > width) {
			if (srcWidth > srcHeight) {
				inSampleSize = Math.round(srcHeight / height);
			} else {
				inSampleSize = Math.round(srcWidth / width);
			}
		}

		options.inJustDecodeBounds = false;
		options.inSampleSize = inSampleSize;

		return BitmapFactory.decodeStream(ins, null, options);
	}

	/**
	 * 获取Resources资源的bitmap对象
	 * 
	 * @param resources   对象
	 * @param resourcesId 资源Id
	 * @param width       宽
	 * @param height      高
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromResource(Resources resources, int resourcesId, int width, int height) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeResource(resources, resourcesId, options);
		float srcWidth = options.outWidth;
		float srcHeight = options.outHeight;
		int inSampleSize = 1;

		if (srcHeight > height || srcWidth > width) {
			if (srcWidth > srcHeight) {
				inSampleSize = Math.round(srcHeight / height);
			} else {
				inSampleSize = Math.round(srcWidth / width);
			}
		}

		options.inJustDecodeBounds = false;
		options.inSampleSize = inSampleSize;

		return BitmapFactory.decodeResource(resources, resourcesId, options);
	}

	/**
	 * 获取缩放后的本地图片
	 *
	 * @param filePath 文件路径,即文件名称
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromAssetsFile(Context context, String filePath) {
		Bitmap image = null;
		AssetManager am = context.getResources().getAssets();
		try {
			InputStream is = am.open(filePath);
			image = BitmapFactory.decodeStream(is);
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return image;
	}

	/**
	 * 从二进制数据中获取bitmap对象
	 * 
	 * @param data   数据
	 * @param width  宽
	 * @param height 高
	 * @return Bitmap
	 */
	public static Bitmap getBitmapFromByteArray(byte[] data, int width, int height) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeByteArray(data, 0, data.length, options);
		float srcWidth = options.outWidth;
		float srcHeight = options.outHeight;
		int inSampleSize = 1;

		if (srcHeight > height || srcWidth > width) {
			if (srcWidth > srcHeight) {
				inSampleSize = Math.round(srcHeight / height);
			} else {
				inSampleSize = Math.round(srcWidth / width);
			}
		}

		options.inJustDecodeBounds = false;
		options.inSampleSize = inSampleSize;

		return BitmapFactory.decodeByteArray(data, 0, data.length, options);
	}

	/**
	 * drawable转换Bitmap
	 * 
	 * @param drawable 对象
	 * @return Bitmap
	 */
	public static Bitmap drawableToBitmap(Context context, int drawableId) {
		Drawable drawable = context.getResources().getDrawable(drawableId);
		Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
				drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
		drawable.draw(canvas);
		return bitmap;
	}

	/**
	 * drawable转换Bitmap
	 * 
	 * @param drawable 对象
	 * @return Bitmap
	 */
	public static Bitmap drawableToBitmap(Drawable drawable) {
		Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
				drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
		Canvas canvas = new Canvas(bitmap);
		drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
		drawable.draw(canvas);
		return bitmap;
	}

	/**
	 * Bitmap转换为Drawable
	 * 
	 * @param resources
	 * @param bm
	 * @return
	 */
	public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
		Drawable drawable = new BitmapDrawable(resources, bm);
		return drawable;
	}

	/**
	 * Return bitmap.
	 *
	 * @param filePath  The path of file.
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return bitmap
	 */
	private static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) {
		if (isSpace(filePath)) {
			return null;
		}
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);
		options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFile(filePath, options);
	}

	/**
	 * Return bitmap.
	 *
	 * @param is The input stream.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final InputStream is) {
		if (is == null) {
			return null;
		}
		return BitmapFactory.decodeStream(is);
	}

	/**
	 * Return bitmap.
	 *
	 * @param is        The input stream.
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) {
		if (is == null) {
			return null;
		}
		byte[] bytes = input2Byte(is);
		return getBitmap(bytes, 0, maxWidth, maxHeight);
	}

	/**
	 * Return bitmap.
	 *
	 * @param data   The data.
	 * @param offset The offset.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final byte[] data, final int offset) {
		if (data.length == 0) {
			return null;
		}
		return BitmapFactory.decodeByteArray(data, offset, data.length);
	}

	/**
	 * Return bitmap.
	 *
	 * @param data      The data.
	 * @param offset    The offset.
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final byte[] data, final int offset, final int maxWidth, final int maxHeight) {
		if (data.length == 0) {
			return null;
		}
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeByteArray(data, offset, data.length, options);
		options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeByteArray(data, offset, data.length, options);
	}

	/**
	 * Return bitmap.
	 *
	 * @param fd The file descriptor.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final FileDescriptor fd) {
		if (fd == null) {
			return null;
		}
		return BitmapFactory.decodeFileDescriptor(fd);
	}

	/**
	 * Return bitmap.
	 *
	 * @param fd        The file descriptor
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return bitmap
	 */
	public static Bitmap getBitmap(final FileDescriptor fd, final int maxWidth, final int maxHeight) {
		if (fd == null) {
			return null;
		}
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFileDescriptor(fd, null, options);
		options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFileDescriptor(fd, null, options);
	}

	/**
	 * Return the sample size.
	 *
	 * @param options   The options.
	 * @param maxWidth  The maximum width.
	 * @param maxHeight The maximum height.
	 * @return the sample size
	 */
	private static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth,
			final int maxHeight) {
		int height = options.outHeight;
		int width = options.outWidth;
		int inSampleSize = 1;
		while ((width >>= 1) >= maxWidth && (height >>= 1) >= maxHeight) {
			inSampleSize <<= 1;
		}
		return inSampleSize;
	}

	/**
	 * 检测是否存在空白符(空格,tab键,换行键)
	 *
	 * @param s
	 * @return
	 */
	private static boolean isSpace(final String s) {
		if (s == null) {
			return true;
		}
		for (int i = 0, len = s.length(); i < len; ++i) {
			if (!Character.isWhitespace(s.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	public static Bitmap getImageFromData(byte[] data, int mMaxWidth, int mMaxHeight) {
		BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
		Bitmap bitmap = null;
		if (mMaxWidth == 0 && mMaxHeight == 0) {
			decodeOptions.inPreferredConfig = Bitmap.Config.RGB_565;
			bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
		} else {
			// If we have to resize this image, first get the natural bounds.
			decodeOptions.inJustDecodeBounds = true;
			BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
			int actualWidth = decodeOptions.outWidth;
			int actualHeight = decodeOptions.outHeight;

			// Then compute the dimensions we would ideally like to decode to.
			int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, actualWidth, actualHeight);
			int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, actualHeight, actualWidth);

			// Decode to the nearest power of two scaling factor.
			decodeOptions.inJustDecodeBounds = false;
			// TODO(ficus): Do we need this or is it okay since API 8 doesn't
			// support it?
			// decodeOptions.inPreferQualityOverSpeed =
			// PREFER_QUALITY_OVER_SPEED;
			decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
			Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);

			// If necessary, scale down to the maximal acceptable size.
			if (tempBitmap != null
					&& (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) {
				bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);
				tempBitmap.recycle();
			} else {
				bitmap = tempBitmap;
			}
		}
		return bitmap;
	}

	public static Bitmap getImageFromBitmap(Bitmap srcBitmap, int mMaxWidth, int mMaxHeight) {
		Bitmap bitmap = null;
		if (mMaxWidth == 0 && mMaxHeight == 0) {
			bitmap = srcBitmap;
		} else {
			int actualWidth = srcBitmap.getWidth();
			int actualHeight = srcBitmap.getHeight();

			// Then compute the dimensions we would ideally like to decode to.
			int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, actualWidth, actualHeight);
			int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, actualHeight, actualWidth);
			bitmap = Bitmap.createScaledBitmap(srcBitmap, desiredWidth, desiredHeight, true);
		}
		return bitmap;
	}

	public static Bitmap getImageFromFile(File file, int mMaxWidth, int mMaxHeight) {
		BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
		Bitmap bitmap = null;
		Bitmap.Config preferredConfig = Bitmap.Config.RGB_565;
		try {
			if (mMaxWidth == 0 && mMaxHeight == 0) {
				bitmap = BitmapFactory.decodeFile(file.getPath());
			} else {
				// If we have to resize this image, first get the natural
				// bounds.
				decodeOptions.inJustDecodeBounds = true;
				decodeOptions.inPreferredConfig = preferredConfig;
				bitmap = BitmapFactory.decodeFile(file.getPath(), decodeOptions);
				int actualWidth = decodeOptions.outWidth;
				int actualHeight = decodeOptions.outHeight;

				// Then compute the dimensions we would ideally like to decode
				// to.
				int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, actualWidth, actualHeight);
				int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, actualHeight, actualWidth);

				// Decode to the nearest power of two scaling factor.
				decodeOptions.inJustDecodeBounds = false;
				// TODO(ficus): Do we need this or is it okay since API 8
				// doesn't
				// support it?
				// decodeOptions.inPreferQualityOverSpeed =
				// PREFER_QUALITY_OVER_SPEED;
				decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
				decodeOptions.inPreferredConfig = preferredConfig;
				Bitmap tempBitmap = BitmapFactory.decodeFile(file.getPath(), decodeOptions);
				// If necessary, scale down to the maximal acceptable size.
				if (tempBitmap != null
						&& (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) {
					bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);
					tempBitmap.recycle();
				} else {
					bitmap = tempBitmap;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}

	public static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, int actualSecondary) {
		// If no dominant value at all, just return the actual.
		if (maxPrimary == 0 && maxSecondary == 0) {
			return actualPrimary;
		}

		// If primary is unspecified, scale primary to match secondary's scaling
		// ratio.
		if (maxPrimary == 0) {
			double ratio = (double) maxSecondary / (double) actualSecondary;
			return (int) (actualPrimary * ratio);
		}

		if (maxSecondary == 0) {
			return maxPrimary;
		}

		double ratio = (double) actualSecondary / (double) actualPrimary;
		int resized = maxPrimary;
		if (resized * ratio > maxSecondary) {
			resized = (int) (maxSecondary / ratio);
		}
		return resized;
	}

	public static int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
		double wr = (double) actualWidth / desiredWidth;
		double hr = (double) actualHeight / desiredHeight;
		double ratio = Math.min(wr, hr);
		float n = 1.0f;
		while ((n * 2) <= ratio) {
			n *= 2;
		}

		return (int) n;
	}

	private static byte[] input2Byte(final InputStream is) {
		if (is == null) {
			return null;
		}
		try {
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			byte[] b = new byte[MemoryConstants.KB];
			int len;
			while ((len = is.read(b, 0, MemoryConstants.KB)) != -1) {
				os.write(b, 0, len);
			}
			return os.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Save the bitmap.
	 *
	 * @param src    The source of bitmap.
	 * @param file   The file.
	 * @param format The format of the image.
	 * @return {@code true}: success<br>
	 *         {@code false}: fail
	 */
	public static boolean save(final Bitmap src, final File file, final Bitmap.CompressFormat format) {
		return save(src, file, format, false);
	}

	/**
	 * Save the bitmap.
	 *
	 * @param src      The source of bitmap.
	 * @param filePath The path of file.
	 * @param format   The format of the image.
	 * @param recycle  True to recycle the source of bitmap, false otherwise.
	 * @return {@code true}: success<br>
	 *         {@code false}: fail
	 */
	public static boolean save(final Bitmap src, final String filePath, final Bitmap.CompressFormat format,
			final boolean recycle) {
		return save(src, getFileByPath(filePath), format, recycle);
	}

	/**
	 * Save the bitmap.
	 *
	 * @param src     The source of bitmap.
	 * @param file    The file.
	 * @param format  The format of the image.
	 * @param recycle True to recycle the source of bitmap, false otherwise.
	 * @return {@code true}: success<br>
	 *         {@code false}: fail
	 */
	public static boolean save(final Bitmap src, final File file, final Bitmap.CompressFormat format,
			final boolean recycle) {
		if (isEmptyBitmap(src) || !createFileByDeleteOldFile(file)) {
			return false;
		}
		OutputStream os = null;
		boolean ret = false;
		try {
			os = new BufferedOutputStream(new FileOutputStream(file));
//            ret = src.compress(format, 100, os);
			ret = src.compress(format, 50, os);
			if (recycle && !src.isRecycled()) {
				src.recycle();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (os != null) {
					os.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return ret;
	}

	private static File getFileByPath(final String filePath) {
		return isSpace(filePath) ? null : new File(filePath);
	}

	private static boolean createFileByDeleteOldFile(final File file) {
		if (file == null) {
			return false;
		}
		if (file.exists() && !file.delete()) {
			return false;
		}
		if (!createOrExistsDir(file.getParentFile())) {
			return false;
		}
		try {
			return file.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}

	private static boolean createOrExistsDir(final File file) {
		return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
	}

	private static boolean isEmptyBitmap(final Bitmap src) {
		return src == null || src.getWidth() == 0 || src.getHeight() == 0;
	}

	/**
	 * 添加时间水印
	 *
	 * @param mBitmap
	 * @return mNewBitmap
	 */
	private static Bitmap getTimeWatermarkPhoto(Bitmap mBitmap) {
		// 获取原始图片与水印图片的宽与高
		int mBitmapWidth = mBitmap.getWidth();
		int mBitmapHeight = mBitmap.getHeight();
		Bitmap mNewBitmap = Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
		Canvas mCanvas = new Canvas(mNewBitmap);
		// 向位图中开始画入MBitmap原始图片
		mCanvas.drawBitmap(mBitmap, 0, 0, null);
		// 添加文字
		Paint mPaint = new Paint();
		String mFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
		// String mFormat = TingUtils.getTime()+"\n"+" 纬度:"+GpsService.latitude+"
		// 经度:"+GpsService.longitude;
		mPaint.setColor(Color.RED);
		mPaint.setTextSize(20);
		// 水印的位置坐标
		mCanvas.drawText(mFormat, (mBitmapWidth * 1) / 10, (mBitmapHeight * 14) / 15, mPaint);
		/* mCanvas.save(Canvas.ALL_SAVE_FLAG); */
		mCanvas.save();
		mCanvas.restore();

		return mNewBitmap;
	}

	/*--------------------------------------*/

	/**
	 * 根据给定的宽和高进行拉伸
	 *
	 * @param origin    原图
	 * @param newWidth  新图的宽
	 * @param newHeight 新图的高
	 * @return new Bitmap
	 */
	public static Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight) {
		if (bitmap == null) {
			return null;
		}
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		float scaleWidth = (float) newWidth / (float) width;
		float scaleHeight = (float) newHeight / (float) height;
		Matrix matrix = new Matrix();
		matrix.postScale(scaleWidth, scaleHeight);
		Bitmap scaleBitmap = null;
		try {
			scaleBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
		} catch (OutOfMemoryError e) {
			while (scaleBitmap == null) {
				System.gc();
				System.runFinalization();
				scaleBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
			}
		}
		recycledBitamp(bitmap);
		return scaleBitmap;
	}

	/**
	 * 按比例缩放图片
	 *
	 * @param origin 原图
	 * @param ratio  比例
	 * @return 新的bitmap
	 */
	private Bitmap scaleBitmap(Bitmap origin, float ratio) {
		if (origin == null) {
			return null;
		}
		int width = origin.getWidth();
		int height = origin.getHeight();
		Matrix matrix = new Matrix();
		matrix.preScale(ratio, ratio);
		Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
		if (newBM.equals(origin)) {
			return newBM;
		}
		recycledBitamp(origin);
		return newBM;
	}

	/**
	 * 裁剪
	 *
	 * @param bitmap 原图
	 * @return 裁剪后的图像
	 */
	private Bitmap cropBitmap(Bitmap bitmap) {
		int w = bitmap.getWidth(); // 得到图片的宽，高
		int h = bitmap.getHeight();
		int cropWidth = w >= h ? h : w;// 裁切后所取的正方形区域边长
		cropWidth /= 2;
		int cropHeight = (int) (cropWidth / 1.2);
		return Bitmap.createBitmap(bitmap, w / 3, 0, cropWidth, cropHeight, null, false);
	}

	/**
	 * 设置图片旋转角度
	 *
	 * @param origin 原图
	 * @param angle  旋转角度，可正可负
	 * @return 旋转后的图片
	 */
	public static Bitmap rotateBitmap(Bitmap origin, float angle) {
		if (origin == null) {
			return null;
		}
		int width = origin.getWidth();
		int height = origin.getHeight();
		Matrix matrix = new Matrix();
		matrix.postRotate(angle);
		// 围绕原地进行旋转
		Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, true);
		recycledBitamp(origin);
		return newBM;
	}

	private static Bitmap rotateImageView(int angle, Bitmap bitmap) {
		// 旋转图片 动作
		Matrix matrix = new Matrix();
		matrix.postRotate(angle);

		// 创建新的图片
		Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
		recycledBitamp(bitmap);
		return resizedBitmap;
	}

	/**
	 * 偏移效果
	 *
	 * @param origin 原图
	 * @return 偏移后的bitmap
	 */
	private Bitmap skewBitmap(Bitmap origin) {
		if (origin == null) {
			return null;
		}
		int width = origin.getWidth();
		int height = origin.getHeight();
		Matrix matrix = new Matrix();
		matrix.postSkew(-0.6f, -0.3f);
		Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
		if (newBM.equals(origin)) {
			return newBM;
		}
		recycledBitamp(origin);
		return newBM;
	}

	/**
	 * YUV 数据转化bitmap
	 * 
	 * @param data   yuv数据
	 * @param width  宽
	 * @param height 高
	 * @return Bitmap
	 */
	public static Bitmap yuv2Bitmap(byte[] data, int width, int height) {
		Bitmap bitmap = null;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
			YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null);
			boolean success = yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, bos);
			if (success) {
				BitmapFactory.Options bitmapFatoryOptions = new BitmapFactory.Options();
				bitmapFatoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;
				byte[] buffer = bos.toByteArray();
				bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, bitmapFatoryOptions);
			}
			bos.flush();
			bos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}

	/**
	 * 回收指定bitmap对象
	 * 
	 * @param bitmap
	 */
	private static void recycledBitamp(Bitmap bitmap) {
		// 先判断是否已经回收
		if (null != bitmap && !bitmap.isRecycled()) {
			// 回收并且置为null
			bitmap.recycle();
			bitmap = null;
			System.gc();
		}
	}

	public static void rotateNv21To90(byte[] src, byte[] des, int width, int height) {
		int wh = width * height;
		// 旋转Y
		int k = 0;
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				des[k] = src[width * (height - j - 1) + i];
				k++;
			}
		}
		for (int i = 0; i < width; i += 2) {
			for (int j = 0; j < height / 2; j++) {
				des[k] = src[wh + width * (height / 2 - j - 1) + i];
				des[k + 1] = src[wh + width * (height / 2 - j - 1) + i + 1];
				k += 2;
			}
		}
	}

	public static void rotateNv21To270(byte[] src, byte[] des, int width, int height) {
		int wh = width * height;
		// 旋转Y
		int k = 0;
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				des[k] = src[width * j + width - i - 1];
				k++;
			}
		}
		for (int i = 0; i < width; i += 2) {
			for (int j = 0; j < height / 2; j++) {
				des[k + 1] = src[wh + width * j + width - i - 1];
				des[k] = src[wh + width * j + width - (i + 1) - 1];
				k += 2;
			}
		}
	}

}
